package com.ticket.sass.common.douyin.openapi

import com.ticket.sass.common.douyin.openapi.enums.GoodsStatus
import com.ticket.sass.common.douyin.openapi.resposne.*
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import java.nio.charset.StandardCharsets
import java.util.*
import java.net.URI
import java.net.URLEncoder


class Client(
    private val clientKey: String,
    private val clientSecret: String,
    private val accountId: String,
    private val podId: String,
) : BaseClient() {

//    private fun generateSign(params: Map<String, String>, httpBody: String): String {
//        val sortedParams = params.toSortedMap()
//        val str1 = buildString {
//            append(clientSecret)
//            sortedParams.forEach { (key, value) ->
//                append("&").append(key).append("=").append(value)
//            }
//            append("&http_body=").append(httpBody)
//        }
//        val sha256Digest: MessageDigest = MessageDigest.getInstance("SHA-256")
//        val hash = sha256Digest.digest(str1.toByteArray(StandardCharsets.UTF_8))
//
//        return Base64.getEncoder().encodeToString(hash)
//    }

    /**
     * 创建client_token
     *
     * client_token 的有效时间为 2 个小时，重复获取 client_token 后会使上次的 client_token 失效（但有 5 分钟的缓冲时间，连续多次获取 client_token 只会保留最新的两个 client_token）。
     * 禁止频繁调用 client_token 接口（频控规则：5 分钟内超过 500 次接口调用，接口报错，错误码 10020）。
     * 正式上线后，测试环境不能使用正式的client_key,client_secret获取token，否则会导致线上正式环境的token失效、影响线上业务
     *
     * 参考: https://developer.open-douyin.com/docs/resource/zh-CN/local-life/develop/OpenAPI/preparation/client_token
     */
    fun createClientToken(): CreateClientTokenData {
        val body: RequestBody = """
            {
                "client_key": "$clientKey",
                "client_secret": "$clientSecret", 
                "grant_type": "client_credential"
            }"
        """.trimIndent()
            .toRequestBody()

        val request = Request.Builder().url(buildUrl("oauth/client_token/")).post(body)
        return doRequest(request, CreateClientTokenData::class.java)
    }

    data class ListGoodsQry(
        // 第一页不传，之后用前一次返回的next_cursor传入进行翻页
        val cursor: String = "",
        // 分页数量，不传默认为5，最大上限50
        val count: Int = 0,
        // 过滤在线状态 1-在线 2-下线 3-封禁
        val status: GoodsStatus = GoodsStatus.ONLINE,
    )

    /**
     * 获取商品列表
     *
     * @param qry 查询条件
     *
     * 参考: https://developer.open-douyin.com/docs/resource/zh-CN/local-life/develop/OpenAPI/goods/online.query
     */
    fun listGoods(qry: ListGoodsQry): ListGoodsData {
        val url = buildUrl("goodlife/v1/goods/product/online/query/").toHttpUrlOrNull()!!.newBuilder()
        url.addQueryParameter("account_id", accountId)
        if (qry.cursor.isNotEmpty()) {
            url.addQueryParameter("cursor", qry.cursor)
        }
        if (qry.count > 0) {
            url.addQueryParameter("count", qry.count.toString())
        }
        url.addQueryParameter("status", qry.status.value.toString())
        val request = Request.Builder().url(url.toString()).get()
        return doRequest(request, ListGoodsData::class.java)
    }

    fun checkAccessToken(): Boolean {
        val r = listGoods(ListGoodsQry(count = 1))
        return r.isSuccess()
    }

    /**
     * 验券准备
     * @param uri 短链
     */
    fun verifyCouponPrepare(uri: URI): VerifyCouponPrepareData {
        val longLink = shortUrlToLongUrl(uri.toString())
        if (longLink.isEmpty()) {
            throw IllegalArgumentException("shortUrl is invalid")
        }
        val encryptedData = getObjectIdFromUrl(longLink) ?: throw IllegalArgumentException("shortUrl is invalid")
        return verifyCouponPrepare(encryptedData = URLEncoder.encode(encryptedData, StandardCharsets.UTF_8.toString()))
    }

    /**
     * 验券准备
     *
     * @param encryptedData 扫码获得
     * @param code 券码
     *
     * 根据certificates
     *
     * 参考: https://developer.open-douyin.com/docs/resource/zh-CN/local-life/develop/OpenAPI/life.capacity.fulfilment/certificate.prepare
     */
    fun verifyCouponPrepare(encryptedData: String = "", code: String = ""): VerifyCouponPrepareData {
        val uri = buildUrl("goodlife/v1/fulfilment/certificate/prepare/").toHttpUrlOrNull()!!.newBuilder()
        if (encryptedData.isNotEmpty()) {
            uri.addQueryParameter("encrypted_data", encryptedData)
        } else if (code.isNotEmpty()) {
            uri.addQueryParameter("code", code)
        }
        val request = Request.Builder().url(uri.toString()).get()
        return doRequest(request, VerifyCouponPrepareData::class.java)
    }

    private fun getObjectIdFromUrl(url: String): String? {
        val uri = URI(url)
        val query = uri.query
        val params = query.split("&").associate {
            val (key, value) = it.split("=")
            key to value
        }
        return params["object_id"]
    }

    /**
     *  短链转长链
     */
    private fun shortUrlToLongUrl(shortUrl: String): String {
        val client = OkHttpClient.Builder()
            .followRedirects(false) // 禁用自动重定向
            .build()
        val request = Request.Builder()
            .url(shortUrl)
            .build()

        client.newCall(request).execute().use { response ->
            if (response.isRedirect) {
                // 获取重定向后的 URL
                return response.header("Location") ?: ""
            }
        }
        return ""
    }

    /**
     * 验券
     *
     * @param verifyToken 验券准备接口返回
     * @param encryptedData 加密后的券码列表
     *
     * error_code、result 均为 0 代表验券成功
     *
     * 参考: https://developer.open-douyin.com/docs/resource/zh-CN/local-life/develop/OpenAPI/life.capacity.fulfilment/certificate.verify#b7d1a248
     */
    fun verifyCoupon(verifyToken: String, encryptedCodes: List<String>): VerifyCouponData {
        val url = buildUrl("goodlife/v1/fulfilment/certificate/verify/")
        val body: RequestBody = """
            {
                "verify_token": "$verifyToken",
                "poi_id": "$podId", 
                "encrypted_codes": ${encryptedCodes.joinToString(",", "[", "]") { "\"$it\"" }}
            }
        """.trimIndent()
            .toRequestBody()

        val request = Request.Builder().url(url).post(body)
        return doRequest(request, VerifyCouponData::class.java)
    }

    /**
     * 查询券码状态
     */
    fun queryCodeStatus(encryptedCode: String) : QueryCodeStatus{
        // 需要url encode
        val formatCode = URLEncoder.encode(encryptedCode, StandardCharsets.UTF_8)
        var url = buildUrl("goodlife/v1/fulfilment/certificate/get/")
        url = "$url?encrypted_code=$formatCode"

        val request = Request.Builder().url(url).get()
        return doRequest(request, QueryCodeStatus::class.java)
    }
}